#ifndef MY_SSESSION_HPP
#define MY_SSESSION_HPP

#include "util.hpp"
#include <unordered_map>

//session管理模块

//session模块

typedef enum {UNLOGIN, LOGIN} ss_status;//用户的两个状态

class session
{
private:

    uint64_t _ssid;//标识符
    uint64_t _uid;//session对应的用户ID
    ss_status _status;//用户状态：未登录，已登录
    wssrv_t::timer_ptr _tp;//session关联的定时器

public:

    session(uint64_t ssid) : _ssid(ssid){ DLOG("session %p 创建成功!", this);}
    ~session(){DLOG("session %p 销毁成功!", this);}
    uint64_t ssid() { return _ssid; }
    void set_user(uint64_t uid) { _uid = uid; }
    void set_status(ss_status status) { _status = status; }
    uint64_t get_user() { return _uid; }
    bool is_login(){ return (_status == LOGIN); }
    void set_timer(const wssrv_t::timer_ptr& tp) { _tp = tp; }
    wssrv_t::timer_ptr& get_timer() { return _tp; }
};



//session管理
//过期时间
#define SESSION_TIMEOUT 30000
//永久存在
#define SESSION_FOREVER -1
using session_ptr = std::shared_ptr<session>;
class session_manager
{
private:
    uint64_t _next_ssid;//session计数器
    std::mutex _mutex;//互斥锁
    std::unordered_map<uint64_t, session_ptr> _session;//管理session的ID与session信息的映射关系
    wssrv_t* _server;//用来添加定时任务
public:
    session_manager(wssrv_t* srv) : _server(srv), _next_ssid(1)
    {
        DLOG("session_manager 创建成功!");
    }

    ~session_manager()
    {
        DLOG("session_manager 销毁成功!");
    }

    session_ptr create_session(uint64_t uid, ss_status status)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_next_ssid));
        if(ssp.get() == nullptr)
        {
            return session_ptr(); 
        }
        ssp->set_user(uid);
        ssp->set_status(status);
        _session.insert(std::make_pair(_next_ssid, ssp));
        _next_ssid++;
        return ssp;
    }

    void append_session(const session_ptr& ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->ssid(), ssp));
    }

    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _session.find(ssid);
        if(it == _session.end())
        {
            return session_ptr();
        }
        return it->second;
    }

    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.erase(ssid);
    }

    void set_session_expire_time(uint64_t ssid, int ms)
    {
        //依赖于websocketpp的定时器来完成session生命周期的管理
        //登录之后,创建session,session需要在指定时间无通信后删除
        //但是进入游戏大厅或者游戏房间，这个session就应该永久存在
        //等到退出游戏大厅或者游戏房间,这个session应该被重新设置为临时,在长时间无通信后被删除

        session_ptr ssp = get_session_by_ssid(ssid);
        if(ssp.get() == nullptr)
        {
            return;
        }
        wssrv_t::timer_ptr tp = ssp->get_timer();
        //1.在session永久存在的情况下，设置永久存在
        if(tp.get() == nullptr && ms == SESSION_FOREVER)
        {
            return;
        }
        //2.在session永久存在的情况下，设置指定时间之后被删除的定时任务
        else if(tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            wssrv_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssp->ssid()));
            ssp->set_timer(tmp_tp);
        }
        //3.在session设置了定时删除的情况下，将session设置为永久存在
        else if(tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            //先撤销这个定时器的任务，然后在重新添加这个session到管理器里面
            tp->cancel();
            //将session的定时器置空
            ssp->set_timer(wssrv_t::timer_ptr());

            //重新添加到session管理器
            //因为撤销不一定立马完成，所以这里使用定时器进行添加
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
        }
        //4.在session设置了定时删除的情况下，将seesion重置删除时间
        else if(tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            //先撤销这个定时器的任务，然后在重新添加这个session到管理器里面
            tp->cancel();
            ssp->set_timer(wssrv_t::timer_ptr());

            //重新添加到session管理器
            //使用定时器进行添加
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
            //添加定时删除任务
            wssrv_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssp->ssid()));
            ssp->set_timer(tmp_tp);
        }
        else
        {
            ELOG("设置过期时间出现错误!");
            return;
        }
    }
};

#endif